#include "delay.h"

#ifndef USE_HAL_DRIVER
static uint32_t uvCount = 0;
static uint32_t start;
#endif

/**
 * @brief System Clock Configuration
 * @retval None
 */
void SystemClock_Config(void)
{

#ifndef USE_HAL_DRIVER
    // 对于STM32F1xx而言，配置系统时钟的时候，需要特别关注FLALSH
    FLASH->ACR |= 0x02 << 0; // 010：两个等待状态，当 48MHz < SYSCLK ≤ 72MHz

    // 配置时钟
    RCC->CR |= 1 << 0; // 1：内部8MHz振荡器开启。
    while ((RCC->CR & (1 << 1)) == 0x00)
        ; // 1：内部8MHz振荡器就绪。

    RCC->CR |= 1 << 16; // 1：HSE振荡器开启
    while ((RCC->CR & (1 << 17)) == 0x00)
        ; // 1：外部4-16MHz振荡器就绪。

    // 配置时钟
    RCC->CFGR |= 1 << 16;    // 选择PLL的时钟源
    RCC->CFGR &= ~(1 << 17); // 0：HSE不分频

    RCC->CFGR &= ~(0x0F << 18);
    RCC->CFGR |= 0x07 << 18; // 0111：PLL 9倍频输出

    RCC->CR |= 1 << 24; // 使能PLL
    while ((RCC->CR & (1 << 25)) == 0x00)
        ; // PLL等待就绪

    // 切换时钟
    RCC->CFGR &= ~(0x03 << 0);
    RCC->CFGR |= 0x02 << 0;

    while ((RCC->CFGR & (0x03 << 2)) != (0x02 << 2))
        ; // 10：PLL输出作为系统时钟；

    // 配置相关分频系数
    RCC->CFGR &= ~(0x0F << 4); // AHB不分频

    RCC->CFGR &= ~(0x07 << 8);
    RCC->CFGR |= (0x04 << 8); // 100：HCLK 2分频

    RCC->CFGR &= ~(0x07 << 11); // 0xx：HCLK不分频

    // 最好直接打开PWR和AFIO
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
#else
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /** Initializes the RCC Oscillators according to the specified parameters
     * in the RCC_OscInitTypeDef structure.
     */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        Error_Handler();
    }

    /** Initializes the CPU, AHB and APB buses clocks
     */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
    {
        Error_Handler();
    }

#endif
}

/**
 * @brief  延迟函数的初始化函数
 * @param  val：外部输入，主系统时钟，单位MHz
 * @retval none
 */
void delay_init(uint16_t val)
{
#ifndef USE_HAL_DRIVER

    // 配置系统时钟
    SystemClock_Config();

    // 配置系统SysTick
    SysTick->LOAD = val * 1000 - 1;     /* set reload register */
    NVIC_SetPriority(SysTick_IRQn, 15); /* set Priority for Systick Interrupt */
    SysTick->VAL = 0UL;                 /* Load the SysTick Counter Value */
    SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
                    SysTick_CTRL_TICKINT_Msk |
                    SysTick_CTRL_ENABLE_Msk;

    // 设置中断优先级分组
    NVIC_SetPriorityGrouping(0x00000003U);
    SystemCoreClock = val * 1000000UL;
#else

    HAL_Init();           // 初始化HAL库
    SystemClock_Config(); // 配置时钟系统

#endif
}

/**
 * @brief This function handles System tick timer.
 */
void SysTick_Handler(void)
{
#ifndef USE_HAL_DRIVER
    uvCount++;
#else
    HAL_IncTick();
#endif
}

/**
 * @brief  毫秒时间延迟
 * @param  val : 需要延迟的ms时间
 * @retval none
 */
void delay_ms(uint32_t val)
{
#ifndef USE_HAL_DRIVER
    start = uvCount;
    while ((uvCount - start) < val)
        ;
#else
    HAL_Delay(val);
#endif
}

/**
 * @brief  微秒时间延迟
 * @param  val : 需要延迟的us时间
 * @retval none
 */
void delay_us(uint32_t val)
{
    uint32_t start = SysTick->VAL; // 先读取

    uint32_t uVal = (val % 1000) * ((SysTick->LOAD + 1) / 1000); // 只处理us部分,需要跳动多少次

    uint32_t dVal = 0;

    do
    {
        if (start > SysTick->VAL)
        { // 这种情况下，是SysTick没有碰到0的情况
            dVal = start - SysTick->VAL;
        }
        else
        {
            dVal = (start - 0) + (SysTick->LOAD - SysTick->VAL);
        }
    } while (dVal < uVal);

    if (val >= 1000)
    {
        delay_ms(val / 1000);
    }
}